#include "tile_table_model.h"

#include <QDebug>
#include <QPixmap>
#include <QPainter>

#include "app_state_model.h"
#include "pack_metadata.h"
#include "texture_pack.h"
#include "tile.h"
#include "tile_sheet.h"
#include "qvariant_utils.h"

const int TileTableModel::kTileSheetRole = Qt::UserRole + 1;
const int TileTableModel::kChosenPackRole = Qt::UserRole + 2;

TileTableModel::TileTableModel(const AppStateModel* app_state,
                               QObject* parent)
    : QAbstractTableModel(parent),
      app_state_model_(app_state),
      default_pack_(app_state->defaultTexturePack()) {
  Q_ASSERT(app_state_model_);
  Q_ASSERT(default_pack_);
}

QString TileTableModel::tileSheetName() const {
  return tilesheet_name_;
}

void TileTableModel::setTileSheetName(QString name) {
  beginResetModel();
  tilesheet_name_ = name;
  selected_packs_.clear();
  endResetModel();
}

void TileTableModel::setDefaultPack(TexturePack* pack) {
  beginResetModel();
  default_pack_ = pack;
  endResetModel();
}

TileSheet* TileTableModel::tileSheet(const QModelIndex& index) const {
  if (!chosenPack(index)) {
    return default_pack_->tileSheetNamed(tilesheet_name_);
  }
  return chosenPack(index)->tileSheetNamed(tilesheet_name_);
}

TexturePack* TileTableModel::chosenPack(const QModelIndex& index) const {
  return selected_packs_.value(index, default_pack_);
}

void TileTableModel::setChosenPack(const QModelIndex& index, TexturePack* pack) {
  Q_ASSERT(pack);
  selected_packs_.insert(index, pack);
  emit dataChanged(index, index);
}

void TileTableModel::setChosenPack(const QModelIndexList &indices, TexturePack* pack) {
  Q_ASSERT(pack);
  QModelIndexList sorted_indices = indices;
  qSort(sorted_indices);
  foreach (const QModelIndex& index, sorted_indices) {
    selected_packs_.insert(index, pack);
  }
  emit dataChanged(sorted_indices.first(), sorted_indices.last());
}

void TileTableModel::chooseNextPack(const QModelIndex& index) {
  TexturePack* pack = chosenPack(index);
  const QList<TexturePack*>* packs = app_state_model_->packs();
  int pack_index = packs->indexOf(pack);
  int next_index = (pack_index + 1) % packs->size();
  TexturePack* next_pack = packs->at(next_index);
  setChosenPack(index, next_pack);
}

QPoint TileTableModel::tilePositionOverride(const QModelIndex& index) const {
  int row = index.row();
  int column = index.column();
  if (tile_position_overrides_.contains(index)) {
    QPoint point = tile_position_overrides_.value(index);
    column = point.x();
    row = point.y();
    return point;
  } else {
    return QPoint(column, row);
  }
}

void TileTableModel::setTilePositionOverride(const QModelIndex& index, const QPoint& override) {
  tile_position_overrides_.insert(index, override);
  emit dataChanged(index, index);
}

void TileTableModel::clearTilePositionOverride(const QModelIndex& index) {
  tile_position_overrides_.remove(index);
  emit dataChanged(index, index);
}

void TileTableModel::clearTilePositionOverride(const QModelIndexList& indices) {
  QModelIndexList sorted_indices = indices;
  qSort(sorted_indices);
  foreach (const QModelIndex& index, sorted_indices) {
    tile_position_overrides_.remove(index);
  }
  emit dataChanged(sorted_indices.first(), sorted_indices.last());
}

QPixmap TileTableModel::tilePixmapOverride(const QModelIndex& index) const {
  if (tile_pixmap_overrides_.contains(index)) {
    return tile_pixmap_overrides_.value(index);
  } else {
    return QPixmap();
  }
}

void TileTableModel::setTilePixmapOverride(const QModelIndex& index, const QPixmap& override) {
  tile_pixmap_overrides_.insert(index, override);
  emit dataChanged(index, index);
}

void TileTableModel::clearTilePixmapOverride(const QModelIndex& index) {
  tile_pixmap_overrides_.remove(index);
  emit dataChanged(index, index);
}

void TileTableModel::clearTilePixmapOverride(const QModelIndexList& indices) {
  QModelIndexList sorted_indices = indices;
  qSort(sorted_indices);
  foreach (const QModelIndex& index, sorted_indices) {
    tile_pixmap_overrides_.remove(index);
  }
  emit dataChanged(sorted_indices.first(), sorted_indices.last());
}

int TileTableModel::rowCount(const QModelIndex& parent) const {
  TileSheet* tilesheet = tileSheet(parent);
  if (!tilesheet) {
    return 0;
  }
  return tilesheet->rows();
}

int TileTableModel::columnCount(const QModelIndex& parent) const {
  TileSheet* tilesheet = tileSheet(parent);
  if (!tilesheet) {
    return 0;
  }
  return tilesheet->columns();
}

QPixmap TileTableModel::tilePixmap(const QModelIndex& index) const {
  const QPixmap& custom_pixmap = tilePixmapOverride(index);
  if (!custom_pixmap.isNull()) {
    return custom_pixmap;
  } else {
    QPoint point = tilePositionOverride(index);
    return tileSheet(index)->tile(point.y(), point.x())->pixmap();
  }
}

QVariant TileTableModel::data(const QModelIndex& index, int role) const {
  if (!index.isValid()) {
    return QVariant();
  }

  if (role == Qt::DisplayRole) {
    return tilePixmap(index);
  } else if (role == Qt::ToolTipRole) {
    const PackMetadata* metadata = app_state_model_->packMetadata();
    return metadata->tileName(tileSheetName(), index.row(), index.column());
  } else if (role == kTileSheetRole) {
    return QVariantPtr<TileSheet>::asVariant(tileSheet(index));
  } else if (role == kChosenPackRole) {
    return QVariantPtr<TexturePack>::asVariant(chosenPack(index));
  } else {
    return QVariant();
  }
}

QVariant TileTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
  Q_UNUSED(section);
  Q_UNUSED(orientation);
  if (role == Qt::SizeHintRole) {
    return QSize(1, 1);
  }
  return QVariant();
}

QPixmap TileTableModel::tileSheetPixmap() const {
  // First determine the size of the pixmap we need to generate.  This will be proportional to the
  // max tile size of this tilesheet for any pack that is used in the model.
  TileSheet* default_sheet =
      app_state_model_->defaultTexturePack()->tileSheetNamed(tileSheetName());
  QSize pixmap_size(default_sheet->pixmap().size());
  QSize tile_size = default_sheet->tileSize();
  foreach (TexturePack* pack, selected_packs_.values()) {
    TileSheet* sheet = pack->tileSheetNamed(tileSheetName());
    pixmap_size = pixmap_size.expandedTo(sheet->pixmap().size());
    tile_size = tile_size.expandedTo(sheet->tileSize());
  }
  QPixmap pixmap(pixmap_size);
  pixmap.fill(Qt::transparent);
  QPainter painter(&pixmap);
  QSize size_in_tiles = app_state_model_->packMetadata()->tilesheetSize(tileSheetName());

  // Sanity check.
  Q_ASSERT(size_in_tiles.width() == pixmap_size.width() / tile_size.width());
  Q_ASSERT(size_in_tiles.height() == pixmap_size.height() / tile_size.height());

  for (int row = 0; row < size_in_tiles.height(); ++row) {
    for (int col = 0; col < size_in_tiles.width(); ++col) {
      QModelIndex index = createIndex(row, col);
      const QPixmap& tile = tilePixmap(index);
      QPoint dest_point(col * tile_size.width(), row * tile_size.height());
      painter.drawPixmap(QRect(dest_point, tile_size), tile);
    }
  }
  return pixmap;
}
